#!/usr/bin/env node

/**
 * read json file
 */
const {
    program
} = require('commander');
console.log('program', program)
program
    .option('-b, --build');
program.parse();

const options = program.opts();

function runNpmShell(shell) {
    const {
        exec
    } = require('child_process');
    exec(shell, (err, stdout, stderr) => {
        if (err) {
            console.log(err);
            return;
        }
        console.log(stdout);
    });
}

const fs = require('fs');
const path = require('path');
const logic = require('logic-helper/server/koa/index.js')
const parser = require('./utils/parser.js')
const eventBus = require('ws-event-bus').default
const jsonDirFile = require("json_dir_file")
const isDev = require('./io/utils.js').isDev;
const whatIsThis = require('./io/utils.js').whatIsThis;
const getObjType = require('./io/utils.js').getObjType;
const defaultConfig = {
    server: {
        host: 'localhost',
        port: 9503,
    },
    mode: "development",
    debug: {
        open: true,
        name: 'logic-topo'
    }
};

function deepMerge(obj, other) {
    if (typeof obj !== "object" || typeof other !== "object") {
        return other;
    }
    if (obj === other) {
        return obj;
    }
    for (let k in other) {
        if (other.hasOwnProperty(k)) {
            if (getObjType(other[k]) === "object") {
                obj[k] = deepMerge(obj[k], other[k]);
            } else {
                obj[k] = other[k];
            }
        }
    }
    return obj;
}

function writeFileToPath(path, content, callback) {
    fs.writeFile(path, content, function (err) {
        if (err) {
            console.log(err);
            return;
        }
        callback && callback();
    });
}

function fileExists(path) {
    var fs = require('fs');
    var exists = fs.existsSync(path);
    return exists;
}

function getConfig() {
    let config = defaultConfig || {};
    if (fileExists(path.resolve(process.cwd(), 'logic.config.js'))) {
        let customed = require(path.resolve(process.cwd(), 'logic.config.js'));
        config = deepMerge(config, customed);
    }
    return config;
}

let throttle;
let cacheResult = ''
const config = getConfig();
let root = path.resolve(process.cwd(), `${'./.logic'}`)
let rootType = whatIsThis(root);
if (!rootType || rootType != 'dir') {
    jsonDirFile.init()
    let obj = jsonDirFile.get(path.resolve(__dirname, './.logic'));
    jsonDirFile.set(root, {}, 2)
    jsonDirFile.set(root, obj, 2)
}

const confFile = path.resolve(process.cwd(), `${'./logic.config.js'}`)
const confFileType = whatIsThis(confFile);
if (!confFileType) {
    const confFileContent = `module.exports = ${JSON.stringify(defaultConfig)}`;
    writeFileToPath(confFile, confFileContent)
}

function shallowEqual(objA, objB) {
    if (objA === objB) {
        return true;
    }
    if (typeof objA !== 'object' || objA === null ||
        typeof objB !== 'object' || objB === null) {
        return false;
    }
    var keysA = Object.keys(objA);
    var keysB = Object.keys(objB);
    if (keysA.length !== keysB.length) {
        return false;
    }
    // Test for A's keys different from B.
    for (var i = 0; i < keysA.length; i++) {
        if (!objB.hasOwnProperty(keysA[i]) || objA[keysA[i]] !== objB[keysA[i]]) {
            return false;
        }
    }
    return true;
}

function createPluginData() {
    jsonDirFile.init()
    let obj = jsonDirFile.get(root);
    const dataStr = parser(obj);
    if (shallowEqual(cacheResult, dataStr)) {
        return;
    }
    cacheResult = dataStr;
    const requireContent = [];
    const exportContent = [];

    let connector = isDev ? `const connector = require('../src/connector.js');\n` : `const connector = require('logic-topo/dist/connector.js');\n`;
    connector += `connector(${`"http://${config.server.host}:${config.server.port}"`});\n`

    for (let filename in dataStr) {
        const info = dataStr[filename];
        writeFileToPath(path.resolve(root, `./${filename}/index.js`), `module.exports = ${JSON.stringify(info)}`);
        requireContent.push(`const id_${filename} = require('./${filename}/index.js')`)
        exportContent.push(`exports.${info.topo.group}_${info.topo.name}= creator(id_${filename})`)
    }
    let file = isDev ? 'const creator = require(\'../src/index.js\');\n' : 'const creator = require(\'logic-topo\');\n';
    if (config.mode == 'development') {
        file += connector;
    }
    requireContent.forEach((item) => {
        file += item + ';\n';
    })
    file += '\n';
    exportContent.forEach((item, index) => {
        file += item + ';\n';
    })
    console.log('createPluginData>>>', file);
    writeFileToPath(path.resolve(root, `index.js`), file);
}

createPluginData();
if (program.args.length) {
    setTimeout(() => {
        const shell = `npm-run-all -p ${program.args.join(' ')}`
        runNpmShell(shell);
        console.log('shell is running: ', shell)
    })
}
const buildShell = program.args.filter((item) => {
    return item.indexOf('build') > -1
})
console.log('buildShell', buildShell)
if (options.build || buildShell.length) {
    return
};
logic.start().then(ws => {
    console.log('logic start')
    let server = new eventBus({
        id: 'task-stream',
        url: `ws://${config.server.host}:${config.server.port}`
    })
    let cache = '';
    server.on('wait', (...arg) => {
        console.log('wait>>>', arg)
        server.waiting.forEach(item => {
            let req = JSON.parse(item.data);
            if (req.eventName == 'setStorage') {
                cache = req.params;
            }
            server.reply(item, JSON.stringify(cache));
            // console.log('reply>>>',cache)
        })
    })
    server.on('message', (e, request) => {
        init = true;
        console.log('message>>>', e, request)
        if (e && e.event == 'dispatch' && e.data && (e.data.action == 'set/logic' || e.data.action == 'init/logic')) {
            clearTimeout(throttle);
            throttle = setTimeout(() => {
                createPluginData()
            }, 1000)
        }
    })
});
